#pragma once
#ifndef OBR_SHADER_FRESNEL_CUH
#define OBR_SHADER_FRESNEL_CUH

#include "../common.cuh"

namespace OBR
{
/**
 * @brief taken from PBRT, eta_i is default to 1.0 (vacuum)
 *
 * @param cos_theta_i
 * @param eta_t
 */
static __forceinline__ __device__ float fr_dielectric(float cos_theta_i, float eta_t)
{
#ifdef USE_APPROX_FRESNEL
    float r0 = (1 - eta_t) / (1 + eta_t) * (1 - eta_t) / (1 + eta_t);
    return r0 + (1.0 - r0) * powf(1.0 - cos_theta_i, 5.0);
#else
    float sin_theta_i = sqrtf(1.0f - cos_theta_i * cos_theta_i);
    float sin_theta_t = 1.0f / eta_t * sin_theta_i;
    float cos_theta_t = sqrtf(1.0f - sin_theta_t * sin_theta_t);

    // Handle total internal reflection
    if (sin_theta_t >= 1.0f)
        return 1.0f;

    float Rparl = ((eta_t * cos_theta_i) - (1.0f * cos_theta_t)) /
                  ((eta_t * cos_theta_i) + (1.0f * cos_theta_t));
    float Rperp = ((1.0f * cos_theta_i) - (eta_t * cos_theta_t)) /
                  ((1.0f * cos_theta_i) + (eta_t * cos_theta_t));
    return (Rparl * Rparl + Rperp * Rperp) / 2.0f;
#endif
}

static __forceinline__ __device__ float fr_conductor(float cos_theta_i, float r0)
{
#ifdef USE_APPROX_FRESNEL
    return r0 + (1.0 - r0) * powf(1.0 - cos_theta_i, 5.0);
#else
    return r0 + (1.0 - r0) * powf(1.0 - cos_theta_i, 5.0);
#endif
}

template <FresnelType ftype>
static __forceinline__ __device__ float fresnel(float cos_theta_i, float coef)
{
    if constexpr (ftype == FresnelType::CONDUCTOR)
    {
        return fr_conductor(cos_theta_i, coef);
    }
    if constexpr (ftype == FresnelType::DIELECTRIC)
    {
        return fr_dielectric(cos_theta_i, coef);
    }
}
} // namespace OBR

#endif
